package com.jzo2o.orders.manager.service.impl;


import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jzo2o.api.customer.AddressBookApi;
import com.jzo2o.api.customer.dto.response.AddressBookResDTO;
import com.jzo2o.api.foundations.ServeApi;
import com.jzo2o.api.foundations.dto.response.ServeAggregationResDTO;
import com.jzo2o.api.market.CouponApi;
import com.jzo2o.api.market.dto.request.CouponUseReqDTO;
import com.jzo2o.api.market.dto.response.AvailableCouponsResDTO;
import com.jzo2o.api.market.dto.response.CouponUseResDTO;
import com.jzo2o.api.trade.NativePayApi;
import com.jzo2o.api.trade.TradingApi;
import com.jzo2o.api.trade.dto.request.NativePayReqDTO;
import com.jzo2o.api.trade.dto.response.NativePayResDTO;
import com.jzo2o.api.trade.dto.response.TradingResDTO;
import com.jzo2o.api.trade.enums.PayChannelEnum;
import com.jzo2o.api.trade.enums.TradingStateEnum;
import com.jzo2o.common.expcetions.CommonException;
import com.jzo2o.common.expcetions.ForbiddenOperationException;
import com.jzo2o.common.utils.BeanUtils;
import com.jzo2o.common.utils.DateUtils;
import com.jzo2o.common.utils.ObjectUtils;
import com.jzo2o.mvc.utils.UserContext;
import com.jzo2o.orders.base.constants.RedisConstants;
import com.jzo2o.orders.base.enums.OrderPayStatusEnum;
import com.jzo2o.orders.base.enums.OrderStatusEnum;
import com.jzo2o.orders.base.mapper.OrdersMapper;
import com.jzo2o.orders.base.model.domain.Orders;
import com.jzo2o.orders.manager.model.dto.request.OrdersPayReqDTO;
import com.jzo2o.orders.manager.model.dto.request.PlaceOrderReqDTO;
import com.jzo2o.orders.manager.model.dto.response.OrdersPayResDTO;
import com.jzo2o.orders.manager.model.dto.response.PlaceOrderResDTO;
import com.jzo2o.orders.manager.porperties.TradeProperties;
import com.jzo2o.orders.manager.service.IOrdersCreateService;
import com.jzo2o.redis.annotations.Lock;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.List;
//导入seata包


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;

/**
 * <p>
 * 下单服务类
 * </p>
 *
 * @author itcast
 * @since 2023-07-10
 */
@Slf4j
@Service
public class OrdersCreateServiceImpl extends ServiceImpl<OrdersMapper, Orders> implements IOrdersCreateService {

    @Autowired
    private CouponApi couponApi;
    @Autowired
    private ServeApi serveApi;

    @Autowired
    private AddressBookApi addressBookApi;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private IOrdersCreateService Ower;

    @Autowired
    private TradingApi tradingApi;

    @Autowired
    private NativePayApi nativePayApi;

    @Autowired
    private TradeProperties tradeProperties;

    /**
     * 下单方法
     * @param dto
     * @return
     */
    @Override
    public PlaceOrderResDTO placeOrder(PlaceOrderReqDTO dto) {
        return Ower.placeOrder(UserContext.currentUserId(),dto);
    }

    @Lock(formatter = "ORDERS:CREATE:LOCK:#{userId}:#{dto.serveId}", waitTime = 1,time = 30, unlock = false)
    @Override
    public PlaceOrderResDTO placeOrder(Long userId,PlaceOrderReqDTO dto) {
        //1.创建一个订单对象
        Orders orders = new Orders();

        //2.封装数据
        orders.setId(generateOrderId());                           // 订单id
        orders.setUserId(UserContext.currentUserId());                       // 订单所属人

        //微服务serve中获取
        ServeAggregationResDTO serveDto = serveApi.findById(dto.getServeId());


        orders.setServeId(serveDto.getId());       // 服务id
        orders.setServeTypeId(serveDto.getServeTypeId());                  // 服务类型id
        orders.setServeTypeName(serveDto.getServeTypeName());                // 服务类型名称
        orders.setServeItemId(serveDto.getServeItemId());                  // 服务项id
        orders.setServeItemName(serveDto.getServeItemName());                // 服务项名称
        orders.setUnit(serveDto.getUnit());                         // 服务单位
        orders.setPrice(serveDto.getPrice());                        // 单价
        orders.setCityCode(serveDto.getCityCode());                     // 城市编码
        orders.setOrdersStatus(0);                 // 订单状态  0待支付
        orders.setPayStatus(2);                    // 交易状态  2待支付
        orders.setPurNum(dto.getPurNum());                       // 购买数量
        orders.setTotalAmount(serveDto.getPrice().multiply(new BigDecimal(dto.getPurNum())));                  // 订单总金额
        orders.setDiscountAmount(new BigDecimal(0));               // 优惠金额
        orders.setRealPayAmount(orders.getTotalAmount().subtract(orders.getDiscountAmount()));                // 实际支付金额

        //微服务customer中获取
        AddressBookResDTO addressDto = addressBookApi.detail(dto.getAddressBookId());

        orders.setServeAddress(addressDto.getAddress());                 // 服务详细地址
        orders.setContactsPhone(addressDto.getPhone());                // 联系人手机号
        orders.setContactsName(addressDto.getName());                 // 联系人姓名
        orders.setLon(addressDto.getLon());                          // 经度
        orders.setLat(addressDto.getLat());                          // 纬度


        orders.setServeStartTime(dto.getServeStartTime());               // 服务开始时间
        orders.setDisplay(1);                      // 用户端是否展示
        orders.setSortBy(DateUtils.toEpochMilli(dto.getServeStartTime())+ orders.getId()%10000);                       // 排序字段




        //判断是否使用了优惠卷
        if(dto.getCouponId()==null)
        {
            //3.保存数据到数据库
            Ower.saveOrder(orders);//这里是为了防止事务失效,所有将本类的Iservice注入到本类
        }else
        {
            Ower.saveOrderWithCoupon(orders,dto.getCouponId());
        }




        //4.返回数据
        return new PlaceOrderResDTO(orders.getId());
    }




    @Transactional
    @Override
    public void saveOrder(Orders orders) {
        this.save(orders);
    }


    /**
     * 支付接口
     * @param id
     * @param ordersPayReqDTO
     * @return
     */

    @Override
    public OrdersPayResDTO pay(Long id, OrdersPayReqDTO ordersPayReqDTO) {
        //1.先根据这个订单id判断订单表中是否有这个订单,有这个订单才生成支付订单
        Orders orders = this.getById(id);
        if(ObjectUtils.isEmpty(orders))
        {
            throw new RuntimeException("订单不存在");
        }

        //2.这个订单的支付状态必须是未支付,第三方支付服务交易订单号也是空的 , 如果存在说明以及调用过这个支付接口了
        if(orders.getTradingOrderNo()!=null&&orders.getPayStatus()==4)
        {
            throw new ForbiddenOperationException("订单已经支付");
        }

        //3.生成支付订单
        NativePayReqDTO nativeDTO = new NativePayReqDTO();//这个就是调用支付接口是所需参数
        nativeDTO.setProductOrderNo(orders.getId());//业务系统订单号
        nativeDTO.setTradingChannel(ordersPayReqDTO.getTradingChannel());//支付渠道
        nativeDTO.setTradingAmount(orders.getRealPayAmount());//交易金额
        nativeDTO.setProductAppId("jzo2o.Orders");//业务系统标识
        nativeDTO.setMemo(orders.getServeItemName());//备注

        if(ordersPayReqDTO.getTradingChannel().equals(PayChannelEnum.WECHAT_PAY))
        {
            //这里设置微信的商户号
            nativeDTO.setEnterpriseId(tradeProperties.getWechatEnterpriseId());//商户号 //判断是微信还是支付宝
        }

        if(ordersPayReqDTO.getTradingChannel().equals(PayChannelEnum.ALI_PAY))
        {
            //这里设置支付宝的商户号
            nativeDTO.setEnterpriseId(tradeProperties.getWechatEnterpriseId());//商户号 //判断是微信还是支付宝
        }


        //是否切换交易渠道 跟上一次的交易渠道是否一致
        //是否切换支付渠道  有可能就是我获取到一个支付码之后,我再退到支付界面换成支付宝再次获取支付链接
        if(orders.getTradingChannel()!=null&&ObjectUtils.notEqual(orders.getTradingChannel(), ordersPayReqDTO.getTradingChannel()))
        {
            nativeDTO.setChangeChannel(true);
        }else
        {
            nativeDTO.setChangeChannel(false);
        }

        NativePayResDTO payResDTO = nativePayApi.createDownLineTrading(nativeDTO);


        //4.跟新数据库 返回封装结果
        //更新order表
        orders.setTradingOrderNo(payResDTO.getTradingOrderNo());//设置交易服务订单号
        orders.setTradingChannel(payResDTO.getTradingChannel());
        this.updateById(orders);

        //更新支付表
        OrdersPayResDTO orderPayDto = BeanUtils.copyBean(payResDTO, OrdersPayResDTO.class);
        orders.setPayStatus(4);
        return orderPayDto;
    }

    /**
     * 获取支付结果
     * @param id
     * @return
     */
    @Override
    public OrdersPayResDTO getPayResultFromTradServer(Long id) {
        //1.判断当前订单是否存在
        Orders orders = this.getById(id);
        if(ObjectUtils.isEmpty(orders))
        {
            throw new RuntimeException("订单不存在");
        }
        //2.订单状态为未支付,且支付服务交易单号不为空  这样才调用支付微服务中的获取支付结果接口
        if(orders.getPayStatus()==2&&orders.getTradingOrderNo()!=null)
        {
            //这里要根据这个微信的支付状态更新订单表中的支付状态
            //调用支付服务查询订单支付状态
            TradingResDTO tradingResDTO = tradingApi.findTradResultByTradingOrderNo(orders.getTradingOrderNo());
            //根据支付服务返回的状态修改订单表中字段(订单状态、支付状态、第三方支付交易号)
            //交易状态: 2-付款中 3-付款失败 4-已结算 5-取消订单
            TradingStateEnum tradingState = tradingResDTO.getTradingState();
            boolean update = this.lambdaUpdate()
                    //交易状态: 4-已结算  订单状态:派单中
                    .set(ObjectUtil.equal(tradingState, TradingStateEnum.YJS), Orders::getOrdersStatus, OrderStatusEnum.DISPATCHING.getStatus())
                    //交易状态: 3-付款失败  订单状态:已关闭
                    .set(ObjectUtil.equal(tradingState, TradingStateEnum.FKSB), Orders::getOrdersStatus, OrderStatusEnum.CLOSED.getStatus())
                    //交易状态: 5-取消订单  订单状态:已取消
                    .set(ObjectUtil.equal(tradingState, TradingStateEnum.QXDD), Orders::getOrdersStatus, OrderStatusEnum.CANCELED.getStatus())
                    //交易状态: 4-已结算  支付状态:支付成功
                    .set(ObjectUtil.equal(tradingState, TradingStateEnum.YJS), Orders::getPayStatus, OrderPayStatusEnum.PAY_SUCCESS.getStatus())
                    //第三方支付交易单号
                    .set(ObjectUtil.isNotEmpty(tradingResDTO.getTransactionId()), Orders::getTransactionId, tradingResDTO.getTransactionId())
                    //根据订单id更新
                    .eq(Orders::getId, id)
                    .update();
            if (!update) {
                log.info("更新订单:{}状态失败", orders.getId());
                throw new CommonException("更新订单" + orders.getId() + "状态失败");
            }
        }


        //订单表中的结果已经更新了,所以这里要从新查询这个支付结果
        Orders newOrders = this.getById(id);

        //封装结果
        OrdersPayResDTO ordersPayResDTO = new OrdersPayResDTO();
        ordersPayResDTO.setPayStatus(newOrders.getPayStatus());//支付状态
        ordersPayResDTO.setProductOrderNo(newOrders.getId());//业务系统订单号
        ordersPayResDTO.setTradingOrderNo(newOrders.getTradingOrderNo());//交易系统订单号
        ordersPayResDTO.setTradingChannel(newOrders.getTradingChannel());//支付渠道

        return ordersPayResDTO;
    }

    @Override

    public List<AvailableCouponsResDTO> getAvailableCoupons(Long serveId, Integer purNum) {
        //1.调用远程的serveApi查询Server对象
        ServeAggregationResDTO serveDTO = serveApi.findById(serveId);
        if(ObjectUtils.isNull(serveDTO))
        {
            throw new ForbiddenOperationException("该服务不存在");
        }

        //2.根据服务的实际价格计算出总的金额
        BigDecimal total = serveDTO.getPrice().multiply(new BigDecimal(purNum));


        //3.调用远程微服务 couponApi
        return  couponApi.getAvailable(total);
    }

    /**
     * 保存订单和核销优惠劵
     * @param orders
     * @param couponId
     */
    @Override
    @GlobalTransactional
    public void saveOrderWithCoupon(Orders orders, Long couponId) {
        //1.调用远程微服务核销优惠劵
        CouponUseReqDTO couponUseReqDTO = new CouponUseReqDTO();
        couponUseReqDTO.setOrdersId(orders.getId());//订单id
        couponUseReqDTO.setId(couponId);//优惠劵id
        couponUseReqDTO.setTotalAmount(orders.getTotalAmount());//总金额

        CouponUseResDTO discountAmount = couponApi.use(couponUseReqDTO);

        //2.修改订单实际的支付金额
        orders.setRealPayAmount(orders.getTotalAmount().subtract(discountAmount.getDiscountAmount()));
        orders.setDiscountAmount(discountAmount.getDiscountAmount());//设置优惠金额

        //保存订单
        this.save(orders);
    }


    //生成订单id
    //规则:生成19位:生成2位年2位月2位日+13位自增序号

    private Long generateOrderId() {
        //获取当前时间6位
        Long yyMMdd = DateUtils.getFormatDate(LocalDateTime.now(), "yyMMdd");
        //自增数字  这里使用redis中的自增数字 increment
        Long num = redisTemplate.opsForValue().increment(RedisConstants.Lock.ORDERS_SHARD_KEY_ID_GENERATOR,1);

        //组装订单id
        return yyMMdd * 10000000000000L + num;
    }
}
